using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
using System.Xml.Serialization;
using AdRotator.Model;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Globalization;
using Microsoft.Xna.Framework.Input.Touch;


#if iOS
using MonoTouch.UIKit;
#endif
#if WINDOWS_PHONE
using Microsoft.Advertising.Mobile.Xna;
using Microsoft.Phone.Net.NetworkInformation;
using System.Windows;
#endif

#if WINDOWS_RT
using System.Threading.Tasks;
using Microsoft.Xna.Framework.Input.Touch;
using Windows.Storage;
using Windows.ApplicationModel;
#else
using System.IO.IsolatedStorage;
#endif


namespace AdRotatorXNA
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class AdRotatorXNAComponent : Microsoft.Xna.Framework.DrawableGameComponent
    {
        private static AdRotatorXNAComponent instance;
        internal SpriteBatch spriteBatch;
        internal static Game game;

        public string DeviceIP = "192.168.0.1";
        
        private bool _loaded = false;
        private bool _initilised = false;
        private bool _started = false;

        public delegate void LogHandler(string message);

        public event LogHandler Log;

        private AdRegistry CurrentRegistry = null;

        public delegate void FetchAdSettingsRemoteThreaded();

        public event FetchAdSettingsRemoteThreaded FetchAdSettingsRemoteThreadedComplete;

#if !WINDOWS_RT
        private Thread AdSettingsLoad;
#endif

        /// <summary>
        /// The displayed ad control instance
        /// </summary>
        private AdComponent _currentAdControl;

        private AdInformation _currentAdInfo;

        /// <summary>
        /// Random generator
        /// </summary>
        private static Random _rnd = new Random();

        /// <summary>
        /// List of ads that should not be displayed. Either because of a failure to load
        /// or they are larger than the alloted space for them
        /// </summary>
        private static List<AdInformation> _invalidAds = new List<AdInformation>();

        /// <summary>
        /// The ad settings based on which the ad descriptor for the current UI culture can be selected
        /// </summary>
        private static AdSettings _settings;

        /// <summary>
        /// The Identifier that should be used to select from the group of ad registries
        /// </summary>
        private static string _currentIdentifier = string.Empty;

        /// <summary>
        /// Indicates whether there has been an attemt to fetch the remote settings file
        /// </summary>
        private static bool _remoteAdSettingsFetched = false;

        /// <summary>
        /// Indicates whether a network has been detected and available, is turned off if none found
        /// </summary>
        private bool _isNetworkAvailable = true;


        #region LoggingEventCode
        protected void OnLog(string message)
        {
            if (Log != null)
                Log(message);

#if DEBUG
            Debug.WriteLine(message);
#endif
        }
        #endregion

        #region SettingsUrl

        /// <summary>
        /// Gets or sets the URL of the remote ad descriptor file
        /// </summary>
        public string SettingsUrl
        {
            get { return SettingsUrlProperty; }
            set 
            { 
                SettingsUrlProperty = value;
                _settingsFileName = Path.GetFileName(new Uri(value).AbsolutePath);
            }
        }

        private static string _settingsFileName = "defaultAdSettings.xml";
        private string SettingsUrlProperty = "";

        #endregion

        #region DefaultAdType

        public AdType DefaultAdType
        {
            get { return (AdType)DefaultAdTypeProperty; }
            set { DefaultAdTypeProperty = value; }
        }

        private AdType DefaultAdTypeProperty = AdType.None;

        #endregion

        #region IsEnabled

        /// <summary>
        /// When set to false the control does not display
        /// </summary>
        public bool IsEnabled
        {
            get { return (bool)IsEnabledProperty; }
            set { IsEnabledProperty = value; }
        }

        private bool IsEnabledProperty = true;

        #endregion

        #region IsTest

        /// <summary>
        /// When set to false the control does not display
        /// </summary>
        public bool IsTest
        {
            get { return (bool)IsTestProperty;}
            set { IsTestProperty = value;}
        }

        private bool IsTestProperty = false;

        #endregion

        #region SlidingAd Properties

        #region SlidingAdDirection

        /// <summary>
        /// Timer to track how long the control has been hidden or displayed
        /// Internal value
        /// </summary>
        private float SlidingAdTimer = 0;

        /// <summary>
        /// Direction the popup will hide / appear from
        /// If not set the AdControl will remain on screen
        /// </summary>
        public SlideDirection SlidingAdDirection
        {
            get { return (SlideDirection)SlidingAdDirectionProperty; }
            set { SlidingAdDirectionProperty = value; }
        }

        private SlideDirection SlidingAdDirectionProperty = SlideDirection.None;

        /// <summary>
        /// Gets the current position of the screen transition, ranging
        /// from zero (fully active, no transition) to one (transitioned
        /// fully off to nothing).
        /// </summary>
        public float TransitionPosition
        {
            get { return transitionPosition; }
            protected set { transitionPosition = value; }
        }

        float transitionPosition = 0;


        /// <summary>
        /// Gets the current alpha of the screen transition, ranging
        /// from 1 (fully active, no transition) to 0 (transitioned
        /// fully off to nothing).
        /// </summary>
        public float TransitionAlpha
        {
            get { return 1f - TransitionPosition; }
        }

        private AdTransitionState _adTransitionState = AdTransitionState.Active;

        public TimeSpan TransitionDuration = TimeSpan.FromSeconds(0.5f);

        private Vector2 _transitionGoalPosition;
        private Vector2 _transitionStartPosition;

        #endregion

        #region SlidingAdDisplaySeconds

        /// <summary>
        /// Amount of time in seconds the ad is displayed on Screen if <see cref="SlidingAdType"/> is set to something else than None
        /// </summary>
        public TimeSpan SlidingAdDisplaySeconds = TimeSpan.FromSeconds(30.0f);

        #endregion

        #region SlidingAdHiddenSeconds

        /// <summary>
        ///  Amount of time in seconds to wait before displaying the ad again 
        ///  (if <see cref="SlidingAdType"/> is set to something else than None).
        ///  Basically the lower this number the more the user is "nagged" by the ad coming back now and again
        /// </summary>
        public TimeSpan SlidingAdHiddenSeconds = TimeSpan.FromSeconds(0);

        #endregion

        #endregion

        #region ADProviderProperties


        #region DefaultHouseAd

        public string DefaultHouseAdRemoteURL
        {
            get
            {
                if (string.IsNullOrEmpty(DefaultHouseAdRemoteURLProperty))
                {
                    DefaultHouseAdRemoteURLProperty = GetAppID(AdType.DefaultHouseAd);
                }
                return (string)DefaultHouseAdRemoteURLProperty;
            }
            set { DefaultHouseAdRemoteURLProperty = value; }
        }

        private string DefaultHouseAdRemoteURLProperty = "";

        public delegate void DefaultHouseAdClickEventHandler();

        public TouchCollection TouchState = new TouchCollection(new TouchLocation[0]);

        public event DefaultHouseAdClickEventHandler DefaultHouseAdClick;
        #endregion

        #region Pubcenter
        #region PubCenterAppId

        public string PubCenterAppId
        {
            get 
            {
                if (string.IsNullOrEmpty(PubCenterAppIdProperty))
                {
                    PubCenterAppIdProperty = GetAppID(AdType.PubCenter);
                }
                return (string)PubCenterAppIdProperty;
            }
            set { PubCenterAppIdProperty = value; }
        }

        private string PubCenterAppIdProperty = "";

        #endregion

        #region PubCenterAdUnitId

        public string PubCenterAdUnitId
        {
            get
            {
                if (string.IsNullOrEmpty(PubCenterAdUnitIdProperty))
                {
                    PubCenterAdUnitIdProperty = GetSecondaryID(AdType.PubCenter);
                }
                return (string)PubCenterAdUnitIdProperty;
            }
            set { PubCenterAdUnitIdProperty = value; }
        }

        private string PubCenterAdUnitIdProperty = ""; //other test values: Image480_80, Image300_50, TextAd 

        #endregion

#if PUBCENTER

#if WINDOWS_RT
#else
        DrawableAd bannerAd;
#endif


#endif

        #endregion

        #region AdDuplexAppId

        public string AdDuplexAppId
        {
            get 
            {
                if (string.IsNullOrEmpty(AdDuplexAppIdProperty))
                {
                    AdDuplexAppIdProperty = GetAppID(AdType.AdDuplex);
                }
                return (string)AdDuplexAppIdProperty;
            }            
            set { AdDuplexAppIdProperty = value; }
        }

        private string AdDuplexAppIdProperty = "";

        #endregion

        #region InneractiveAppId

        public string InneractiveAppId
        {
            get 
            {
                if (string.IsNullOrEmpty(InneractiveAppIdProperty))
                {
                    InneractiveAppIdProperty = GetAppID(AdType.InnerActive);
                }
                return (string)InneractiveAppIdProperty;
            }   
            set { InneractiveAppIdProperty = value; }
        }

        private string InneractiveAppIdProperty = string.Empty;

        #endregion

        #region MobFox

        public string MobFoxAppId
        {
            get 
            {
                if (string.IsNullOrEmpty(MobFoxAppIdProperty))
                {
                    MobFoxAppIdProperty = GetAppID(AdType.MobFox);
                }
                return (string)MobFoxAppIdProperty;
            }  
            set { MobFoxAppIdProperty = value; }
        }

        private string MobFoxAppIdProperty = string.Empty;


        public bool MobFoxIsTest
        {
            get { return (bool)MobFoxIsTestProperty; }
            set { MobFoxIsTestProperty = value; }
        }

        private bool MobFoxIsTestProperty = false;
                
        #endregion

        #region Smaato

        #region SmaatoPublisherId

        public string SmaatoPublisherId
        {
            get
            {
                if (string.IsNullOrEmpty(SmaatoPublisherIdProperty))
                {
                    SmaatoPublisherIdProperty = GetSecondaryID(AdType.Smaato);
                }
                return (string)SmaatoPublisherIdProperty;
            }
            set { SmaatoPublisherIdProperty = value; }
        }

        private string SmaatoPublisherIdProperty = string.Empty;
        #endregion

        #region SmaatoAppId
        public string SmaatoAppId
        {
            get
            {
                if (string.IsNullOrEmpty(SmaatoAppIdProperty))
                {
                    SmaatoAppIdProperty = GetAppID(AdType.Smaato);
                }
                return (string)SmaatoAppIdProperty;
            }
            set { SmaatoAppIdProperty = value; }
        }

        private string SmaatoAppIdProperty = string.Empty;

        #endregion

        #endregion

        #endregion

        #region Ad Placement Settings

        private Rectangle _destRect;
        
        #endregion


        private AdRotatorXNAComponent(Game game)
            : base(game)
        {
        }

        public static AdRotatorXNAComponent Current
        {
            get
            {
                if (game == null)
                {
                    return null;
                }
                if (instance == null)
                {
                    instance = new AdRotatorXNAComponent(game);
                }

                return instance;
            }
        }

        #region XNA Specific

        public static void Initialize(Game _game, string settingsURL)
        {
            game = _game;

            Current.SettingsUrl = settingsURL;

            Current.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: Add your initialization code here
#if WINDOWS_PHONE
            if (!NetworkInterface.GetIsNetworkAvailable() || NetworkInterface.NetworkInterfaceType == NetworkInterfaceType.None)
            {
                //Would be good to add an all encompasing APP Based Fail AD, like an App ID Image
                this._isNetworkAvailable = false;
            }		
#endif
            _loaded = true;

#if WINDOWS_RT
            Task.Run(() =>
            {
                FetchAdSettingsFile();
            });
#else
            instance.AdSettingsLoad = new Thread(new ThreadStart(instance.FetchAdSettingsFile));
            instance.AdSettingsLoad.Name = "FetchRemoteAdSettingsThread";
            instance.FetchAdSettingsRemoteThreadedComplete += instance.AdRotatorXNAComponent_FetchAdSettingsRemoteThreadedComplete;

            instance.AdSettingsLoad.Start();
#endif
            base.LoadContent();
        }

        public void Disable()
        {
            _adTransitionState = AdTransitionState.Hidden;
            Enabled = false;
            Invalidate(false);
        }

                /// <summary>
        /// Enables the ad service to display ads at the dest rect. Ads that would violate the destrect are not loaded.
        /// </summary>
        /// <param name="destRect">Rectangle that ads should center themselves in.</param>
        /// <param name="adRegistryID">Identifier of the group to load</param>
        public void Enable(Rectangle destRect, string adRegistryID)
        {
            Enabled = true;
            _currentIdentifier = adRegistryID;
            _destRect = destRect;
            Invalidate(true);
            _adTransitionState = AdRotatorXNA.AdTransitionState.Hidden;
            transitionPosition = 0.0f;
        }

        void AdRotatorXNAComponent_FetchAdSettingsRemoteThreadedComplete()
        {
            FetchAdSettingsRemoteThreadedComplete -= AdRotatorXNAComponent_FetchAdSettingsRemoteThreadedComplete;

#if !WINDOWS_RT
            AdSettingsLoad = null;
#endif
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        protected override void OnUpdateOrderChanged(object sender, EventArgs args)
        {
            if (_currentAdControl != null)
                ((DrawableGameComponent)_currentAdControl).UpdateOrder = this.UpdateOrder;
            base.OnUpdateOrderChanged(sender, args);
        }

        protected override void OnEnabledChanged(object sender, EventArgs args)
        {
            if (_currentAdControl != null)
                ((DrawableGameComponent)_currentAdControl).Enabled = this.Enabled;
            base.OnEnabledChanged(sender, args);
        }

        protected override void OnVisibleChanged(object sender, EventArgs args)
        {
            if (_currentAdControl != null)
                ((DrawableGameComponent)_currentAdControl).Visible = this.Visible;
            base.OnVisibleChanged(sender, args);
        }

        protected override void OnDrawOrderChanged(object sender, EventArgs args)
        {
            if (_currentAdControl != null)
                ((DrawableGameComponent)_currentAdControl).DrawOrder = this.DrawOrder;
            base.OnDrawOrderChanged(sender, args);
        }


        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            // TODO: Add your update code here
            if (!_started && _initilised)
            {
                _started = true;
                Init();
            }

            // No work to do if there's no control set, or we are waiting on the ad
            // to load
            if (_currentAdControl == null)
            {
                base.Update(gameTime);
                return;
            }
            
            if (_adTransitionState == AdTransitionState.Hidden && _currentAdControl.State != AdState.AdLoaded)
            {
                base.Update(gameTime);
                return;
            }

            //Check Current Ad State and transition as required
            if (SlidingAdDirection != SlideDirection.None && _loaded)
            {
                switch (_adTransitionState)
                {
                    case AdTransitionState.TransitionOn:
                        // Otherwise the screen should transition on and become active.
                        if (UpdateTransition(gameTime, TransitionDuration, 1))
                        {
                            // Still busy transitioning.
                            _adTransitionState = AdTransitionState.TransitionOn;
                        }
                        else
                        {
                            // Transition finished!
                            _adTransitionState = AdTransitionState.Active;
                            SlidingAdTimer = 0;
                        }
                        break;
                    case AdTransitionState.Active:
                        if (SlidingAdTimer >= SlidingAdDisplaySeconds.TotalMilliseconds)
                        {
                            // Finished displaying, hide Ad
                            _adTransitionState = AdTransitionState.TransitionOff;
                            SlidingAdTimer = 0;
                        }
                        else
                        {
                            SlidingAdTimer += (float)gameTime.ElapsedGameTime.TotalMilliseconds;
                        }
                        break;
                    case AdTransitionState.TransitionOff:
                        // If the screen is covered by another, it should transition off.
                        if (UpdateTransition(gameTime, TransitionDuration, -1))
                        {
                            // Still busy transitioning.
                            _adTransitionState = AdTransitionState.TransitionOff;
                        }
                        else
                        {
                            // Transition finished!
                            Invalidate(true);
                            _adTransitionState = AdTransitionState.Hidden;
                            SlidingAdTimer = 0;
                        }
                        break;
                    case AdTransitionState.Hidden:
                        if (SlidingAdTimer >= SlidingAdHiddenSeconds.TotalMilliseconds)
                        {
                            // Ad ready to display
                            _adTransitionState = AdTransitionState.TransitionOn;
                            SlidingAdTimer = 0;

                        }
                        else
                        {
                            SlidingAdTimer += (float)gameTime.ElapsedGameTime.TotalMilliseconds;
                        }
                        break;
                    default:
                        break;
                }


                // set the entry's position
                _currentAdControl.UpdatePosition(Vector2.Lerp(_transitionStartPosition, _transitionGoalPosition, TransitionPosition));
            }
            base.Update(gameTime);
        }

        private void UpdateTransitionPositions()
        {
            var vp = game.GraphicsDevice.Viewport;
            var adinfo = _currentAdControl.AdInfo;

            _transitionGoalPosition = new Vector2(_destRect.X + ((_destRect.Width - adinfo.Width) * 0.5f),
                                                  _destRect.Y + ((_destRect.Height - adinfo.Height) * 0.5f));

            switch (SlidingAdDirection)
            {
                case SlideDirection.Top:
                    _transitionStartPosition = new Vector2(_transitionGoalPosition.X, -_destRect.Height);
                    break;
                case SlideDirection.Bottom:
                    _transitionStartPosition = new Vector2(_transitionGoalPosition.X, vp.Height + _destRect.Height);
                    break;
                case SlideDirection.Left:
                    _transitionStartPosition = new Vector2(-_destRect.Width, _transitionGoalPosition.Y);
                    break;
                case SlideDirection.Right:
                    _transitionStartPosition = new Vector2(vp.Width + _destRect.Width, _transitionGoalPosition.Y);
                    break;

                case SlideDirection.None:
                    _transitionStartPosition = _transitionGoalPosition;
                    break;

                default:
                    break;
            }
        }

        /// <summary>
        /// Helper for updating the screen transition position.
        /// </summary>
        bool UpdateTransition(GameTime gameTime, TimeSpan time, int direction)
        {
            // How much should we move by?
            float transitionDelta;

            if (time == TimeSpan.Zero)
                transitionDelta = 1;
            else
                transitionDelta = (float)(gameTime.ElapsedGameTime.TotalMilliseconds /
                                          time.TotalMilliseconds);

            // Update the transition position.
            transitionPosition += transitionDelta * direction;

            // Did we reach the end of the transition?
            if (((direction < 0) && (transitionPosition <= 0)) ||
                ((direction > 0) && (transitionPosition >= 1)))
            {
                transitionPosition = MathHelper.Clamp(transitionPosition, 0, 1);
                return false;
            }

            // Otherwise we are still busy transitioning.
            return true;
        }


        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Draw(GameTime gameTime)
        {
            // TODO: Add your drawing code here

            base.Draw(gameTime);
        }
        #endregion

        /// <summary>
        /// Fetches the ad settings file from the address specified at <see cref=""/>
        /// </summary>
        public void FetchAdSettingsFile()
        {
            if (_remoteAdSettingsFetched || !IsEnabled )
                return;

            if (!_isNetworkAvailable || String.IsNullOrEmpty(SettingsUrl))
                _settings = LoadAdSettings();
            else
            {
                var request = (HttpWebRequest)WebRequest.Create(new Uri(SettingsUrl));
                request.BeginGetResponse(r =>
                {
                    try
                    {
                        var httpRequest = (HttpWebRequest)r.AsyncState;
                        var httpResponse = (HttpWebResponse)httpRequest.EndGetResponse(r);
                        var settingsStream = httpResponse.GetResponseStream();

                        var s = new System.Xml.Serialization.XmlSerializer(typeof(AdSettings));
                        _settings = (AdSettings)s.Deserialize(settingsStream);

                        // Only persist the settings if they've been retreived from the remote file
                        SaveAdSettings(_settings);
                        OnLog("Setings retrieved from remote");
                    }
                    catch
                    {
                        OnLog("Failed setings retrieved from remote, trying local");
                        _settings = LoadAdSettings();  
                    }
                    finally
                    {
                        _remoteAdSettingsFetched = true;
                        CurrentRegistry = UpdateRegistry();
                        _initilised = true;
                    }
                }, request);
            }
        }

        /// <summary>
        /// Displays a new ad
        /// </summary>
        /// <param name="selectNextAdType">If set to true, selects the next ad type in order, otherwise chooses 
        ///     a random one that hasn't had issues loading previously</param>
        /// <returns></returns>
        public string Invalidate(bool selectNextAdType)
        {
            string adTypeName = "";

            if (!_loaded || !_initilised)
            {
                OnLog("Control tested before loaded");
                return "";
            }
            if (!IsEnabled)
            {
                OnLog("Ad control disabled");
                return adTypeName;
            }
            else
                OnLog("Ads are enabled for display");

            this.Game.Components.Remove(_currentAdControl);

            if (_currentAdControl != null && _currentAdControl.Enabled)
                _currentAdControl.Enabled = false;

            if (_isNetworkAvailable)
            {
                if (selectNextAdType)
                    _currentAdInfo = GetNextAdType();
                else
                {
                    OnLog("Ad control disabled");
                    _currentAdControl = null;
                    return string.Empty;
                }
            }
            else
            {
                CurrentRegistry = CurrentRegistry == null ? UpdateRegistry() : CurrentRegistry;
                if (CurrentRegistry == null)
                {
                    //Failed to get any add configuration, control disabled
                    _currentAdInfo = null;
                    Enabled = false;
                }
                else
                    _currentAdInfo = CurrentRegistry.AdInformations.Where(x => x.AdType == AdType.DefaultHouseAd).FirstOrDefault();
            }

            OnLog(string.Format("Ads being requested for: {0}", _currentAdInfo));

            if (_currentAdInfo == null)
            {
                // There was nothing to display. Disable the control.
                Enabled = false;
                return null;
            }

            switch (_currentAdInfo.AdType)
            {
                case AdType.PubCenter:
#if PUBCENTER
                    _currentAdControl = CreatePubCentertAdControl(_currentAdInfo);
#endif
                    break;
                case AdType.AdDuplex:
#if ADDUPLEX
                    _currentAdControl = CreateAdDuplexControl();
#endif
                    break;
                case AdType.InnerActive:
#if ADINNERACTIVE
                    _currentAdControl = CreateInneractiveControl();
#endif
                    break;
                case AdType.MobFox:
#if MOBFOX
                    _currentAdControl = CreateMobFoxControl();
#endif
                    break;
                case AdType.Smaato:
#if SMATTO
                    _currentAdControl = CreateSmaatoControl();
#endif
                    break;
                case AdType.AdMob:
#if ADMOB
                _currentAdControl = CreateAdMobControl(_currentAdInfo);
#endif
                break;
                
#if iOS
                case AdType.iAd:
                    _currentAdControl = CreateiAdControl(_currentAdInfo);
                    break;
#endif
                
                case AdType.DefaultHouseAd:
                    _currentAdControl = CreateDefaultHouseAdControl(_currentAdInfo);
                    break;
                
                default:
                    _currentAdControl = null;
                    return adTypeName;
            }
            if (_currentAdControl == null)
            {
                OnLog(string.Format("Ads failed for: {0}", _currentAdInfo.AdType));
                adTypeName = "failed - " + _currentAdInfo.AdType.ToString();
                OnAdLoadFailed(_currentAdInfo);
                return adTypeName;
            }

            _currentAdControl.UpdateAd(_currentAdInfo);

            ApplyXNAProperties(_currentAdControl);

            AddEventHandlersToAdControl();
            this.Game.Components.Add(_currentAdControl);

            UpdateTransitionPositions();
            // Ensure that if we are transitioning, our ad begins offscreen.
            _currentAdControl.UpdatePosition(_transitionStartPosition);

            if (_currentAdInfo != null && _currentAdControl != null)
            {
                adTypeName = _currentAdInfo.AdType.ToString();
                OnLog(string.Format("Ads being served for: {0}", _currentAdInfo.AdType.ToString()));
                OnAdLoadSucceeded(_currentAdInfo);
            }
            else
                OnLog("No ads available, nothing to show");

            return adTypeName;
        }

        private void ApplyXNAProperties(GameComponent CurrentAdControl)
        {
            ((DrawableGameComponent)CurrentAdControl).Enabled = this.Enabled;
            ((DrawableGameComponent)CurrentAdControl).Visible = this.Visible;
            ((DrawableGameComponent)CurrentAdControl).DrawOrder = this.DrawOrder;
            ((DrawableGameComponent)CurrentAdControl).UpdateOrder = this.UpdateOrder;
        }

        /// <summary>
        /// Generates the next ad to display
        /// </summary>
        /// <returns></returns>
        private AdInformation GetNextAdType()
        {
            CurrentRegistry = UpdateRegistry();

            if (CurrentRegistry == null)
                return null;

            var validDescriptors = CurrentRegistry.AdInformations
                .Where(x => !_invalidAds.Contains(x)
                            && IsAdValid(x)).ToList();

            if (validDescriptors.Count == 0)
                return null;

            var totalValueBetweenValidAds = validDescriptors.Sum(x => x.ProbabilityValue);
            var randomValue = _rnd.NextDouble() * totalValueBetweenValidAds;
            double totalCounter = 0;
            foreach (var probabilityDescriptor in validDescriptors)
            {
                totalCounter += probabilityDescriptor.ProbabilityValue;
                if (randomValue < totalCounter)
                    return probabilityDescriptor;
            }

            var DefaultHouseAd = validDescriptors.Where(x => x.AdType == AdType.DefaultHouseAd).First();
            if (DefaultHouseAd != null)
                return DefaultHouseAd;

            return null;
        }

        /// <summary>
        /// Called when the settings have been loaded. Clears all failed ad types and invalidates the control
        /// </summary>
        private void Init()
        {
            _invalidAds.Clear();
            Invalidate(Enabled);
            _adTransitionState = AdRotatorXNA.AdTransitionState.Hidden;
        }

        private bool IsAdValid(AdInformation ad)
        {
            // An ad isn't valid if it wouldn't fit in the provided destrect
            var vp = GraphicsDevice.Viewport;

            if (_destRect.Width < ad.Width ||
                _destRect.Height < ad.Height ||
                _destRect.X + ad.Width > vp.Width ||
                _destRect.Y + ad.Height > vp.Height)
                return false;

            return true;
        }

        private void AddEventHandlersToAdControl()
        {
            _currentAdControl.AdLoaded += OnAdLoadSucceeded;
            _currentAdControl.AdLoadingFailed += OnAdLoadFailed;
        }

        private void DisableAdControl()
        {

#if ADDUPLEX
            var adDuplexAd = _currentAdControl as AdDuplexComponent;
            if (adDuplexAd != null)
            {
                AdDuplexComponent.Current.AdLoaded -= Current_AdDuplex_AdLoaded;
                AdDuplexComponent.Current.AdLoadingFailed -= Current_AdDuplex_AdLoadingFailed;
                AdDuplexComponent.Current.AdClicked -= Current_AdDuplex_AdClicked;
            }
#endif

#if ADINNERACTIVE
            var innerativeAd = _currentAdControl as AdInneractiveComponent;
            if (innerativeAd != null)
            {
                AdInneractiveComponent.Current.AdLoaded -= Current_Inneractive_AdLoaded;
                AdInneractiveComponent.Current.AdLoadingFailed -= Current_Inneractive_AdLoadingFailed;
                AdInneractiveComponent.Current.AdClicked -= Current_Inneractive_AdClicked;
            }
            var MobFoxAd = _currentAdControl as AdMobFoxComponent;
            if (MobFoxAd != null)
            {
                AdMobFoxComponent.Current.AdLoaded -= Current_MobFox_AdLoaded;
                AdMobFoxComponent.Current.AdLoadingFailed -= Current_MobFox_AdLoadingFailed;
                AdMobFoxComponent.Current.AdClicked -= Current_MobFox_AdClicked;
            }
            var DefaultHouseAd = _currentAdControl as AdRotatorDefaultHouseAd;
            if (DefaultHouseAd != null)
            {
                AdRotatorDefaultHouseAd.Current.AdClicked -= Current_DefaultHouseAd_AdClicked;
            }
#endif

#if SMATTO
            var SmaatoAd = _currentAdControl as AdSmaatoComponent;
            if (SmaatoAd != null)
            {
                AdSmaatoComponent.Current.AdLoaded -= Current_Smaato_AdLoaded;
                AdSmaatoComponent.Current.AdLoadingFailed -= Current_Smaato_AdLoadingFailed;
                AdSmaatoComponent.Current.AdClicked -= Current_Smaato_AdClicked;
            }
#endif
        }

        /// <summary>
        /// Updates the ad descriptor based on the current UI culture and identifier.
        /// </summary>
        /// <returns></returns>
        private AdRegistry UpdateRegistry()
        {
            if (_settings == null || _settings.AdRegistries == null)
                return null;

            var ci = CultureInfo.CurrentCulture;
            var cultureLongName = ci.DisplayName;

            var registries = _settings.AdRegistries.Where(x => x.Identifier == _currentIdentifier);

            if (registries.Count() == 0)
                return null;

            var sameLanguageDescriptor = registries.Where(x => x.CultureName.StartsWith(ci.TwoLetterISOLanguageName)).FirstOrDefault();
            if (sameLanguageDescriptor != null)
            {
                SlidingAdDisplaySeconds = TimeSpan.FromSeconds(sameLanguageDescriptor.DisplaySeconds);
                return sameLanguageDescriptor;
            }

            var defaultDescriptor = registries.Where(x => x.CultureName == AdSettings.DEFAULT_CULTURE).FirstOrDefault();
            if (defaultDescriptor != null)
            {
                SlidingAdDisplaySeconds = TimeSpan.FromSeconds(defaultDescriptor.DisplaySeconds);
                return defaultDescriptor;
            }

            // Couldn't find anything, but the list had something in it. Cross our fingers and return the first.
            return registries.FirstOrDefault();
        }

        /// <summary>
        /// Called when <paramref name="adType"/> has failed to load
        /// </summary>
        /// <param name="adType"></param>
        private void OnAdLoadFailed(AdInformation adInfo)
        {
            if (adInfo == null)
                return;

            OnLog(string.Format("Ads failed request for: {0}", adInfo.ToString()));
            if (!_invalidAds.Contains(adInfo))
                _invalidAds.Add(adInfo);

            if (_currentAdControl == null || _currentAdControl.AdInfo != adInfo)
                return;

            // If an ad couldn't be displayed, slide it out and let the next slide in.
            if (_adTransitionState == AdTransitionState.Hidden)
                Invalidate(true);
            else
                SlidingAdTimer = (float)SlidingAdDisplaySeconds.TotalMilliseconds;
        }

        /// <summary>
        /// Called when <paramref name="adType"/> has succeeded to load
        /// </summary>
        /// <param name="adType"></param>
        private void OnAdLoadSucceeded(AdInformation adInfo)
        {
            // Begin our transition once our ad's ready to display.
            SlidingAdTimer = (float)SlidingAdHiddenSeconds.TotalMilliseconds;
            
            OnLog(string.Format("Ads being successfully served for: {0}", adInfo.ToString()));
            
            // Just because an ad failed, doesn't mean it will in the future.
            // When we've got a valid ad onscreen, clear our invalid ads list
            // so that it may be considered for the next rotation.
            _invalidAds.Clear();
        }

        private string GetAppID(AdType adType)
        {
            return CurrentRegistry.AdInformations
                    .Where(x => x.AdType == adType)
                    .First().AppID;
        }

        private string GetSecondaryID(AdType adType)
        {
            return CurrentRegistry.AdInformations
                    .Where(x => x.AdType == adType)
                    .First().SecondaryID;
        }

        private Vector2 GetSize (AdType adType)
        {
            var first = CurrentRegistry.AdInformations
                    .Where(x => x.AdType == adType)
                    .First();

            return new Vector2(first.Width, first.Height);
        }

        /// <summary>
        /// Loads the ad settings object either from isolated storage or from the resource path defined in DefaultSettingsFileUri.
        /// </summary>
        /// <returns></returns>
        private AdSettings LoadAdSettings()
        {

#if WINDOWS_RT
            var output = Task.Run(
                async () =>
                {

                    try
                    {
                        var localFolder = ApplicationData.Current.LocalFolder;
                        var storageFile = await localFolder.GetFileAsync(_settingsFileName);

                        using (var fs = await storageFile.OpenStreamForReadAsync())
                        {
                            var xs = new XmlSerializer(typeof(AdSettings));
                            try
                            {
                                return (AdSettings)xs.Deserialize(fs);
                            }
                            catch
                            {
                                return null;
                            }
                        }
                    }
                    catch (IOException)
                    {
                        return null;
                    }

                }).Result;

            return output;
#else
            try
            {
                var isfData = IsolatedStorageFile.GetUserStoreForApplication();
                IsolatedStorageFileStream isfStream = null;
                if (isfData.FileExists(_settingsFileName))
                {
                    using (isfStream = new IsolatedStorageFileStream(_settingsFileName, FileMode.Open, isfData))
                    {
                        XmlSerializer xs = new XmlSerializer(typeof(AdSettings));
                        try
                        {
                            var settings = (AdSettings)xs.Deserialize(isfStream);
                            return settings;
                        }
                        catch
                        {

                        }
                    }
                }
            }
            catch (IsolatedStorageException)
            {
            }
#endif


            if (_settings == null)
            {
                _settings = GetDefaultSettings();
                OnLog("Failed Setings retrieved from local trying defaults");
            }
            return _settings;
        }

        private AdSettings GetDefaultSettings()
        {

            var xs = new XmlSerializer(typeof(AdSettings));
            try
            {
                var defaultSettingsFileInfo = TitleContainer.OpenStream(_settingsFileName);
                var settings = (AdSettings)xs.Deserialize(defaultSettingsFileInfo);
                return settings;
            }
            catch (Exception e)
            {
                var error = e;
                OnLog("Loading Defaults failed, control disabled");
            }

            return new AdSettings();

        }

        /// <summary>
        /// Saves the passed settings file to isolated storage
        /// </summary>
        /// <param name="settings"></param>
        private static void SaveAdSettings(AdSettings settings)
        {
#if WINDOWS_RT
            try
            {

                using (var stream = Task.Run(
                    async () =>
                    {
                        var storageFile = await ApplicationData.Current.LocalFolder.CreateFileAsync(_settingsFileName, CreationCollisionOption.ReplaceExisting);
                        return await storageFile.OpenStreamForWriteAsync();

                    }).Result)
                {
                    var xs = new XmlSerializer(typeof(AdSettings));
                    xs.Serialize(stream, settings);
                }

            }
            catch
            {
            }
#else
            try
            {
                XmlSerializer xs = new XmlSerializer(typeof(AdSettings));
                using (IsolatedStorageFileStream isfStream = new IsolatedStorageFileStream(_settingsFileName, FileMode.Create, IsolatedStorageFile.GetUserStoreForApplication()))
                {
                    xs.Serialize(isfStream, settings);
                }
            }
            catch
            {
            }
#endif

        }

        #region Specific ad controls

        #region DefaultHouseAd
        private AdComponent CreateDefaultHouseAdControl(AdInformation adInfo)
        {
            try
            {
                // Initialize the AdGameComponent with your ApplicationId and add it to the game. 
                AdRotatorDefaultHouseAd.Initialize(this.Game, adInfo.AppID);

                if (string.IsNullOrEmpty(adInfo.SecondaryID))
                    AdRotatorDefaultHouseAd.Current.AdClicked += Current_DefaultHouseAd_AdClicked;
                else
                    AdRotatorDefaultHouseAd.Current.AdClicked += (info) =>
                    {
#if WINDOWS_PHONE
                        var page = new Microsoft.Phone.Tasks.WebBrowserTask { Uri = new Uri(info.SecondaryID) };
                        page.Show();
#elif WINDOWS_RT     
                
                        LaunchURL(new Uri(info.SecondaryID));
#endif
                    };
                return AdRotatorDefaultHouseAd.Current;
            }
            catch (Exception) { return null; }
        }

        void Current_DefaultHouseAd_AdClicked(object sender)
        {
            if (DefaultHouseAdClick != null)
                DefaultHouseAdClick();
        }

#if WINDOWS_RT
        static async void LaunchURL(Uri uri)
        {
            var success = await Windows.System.Launcher.LaunchUriAsync(uri);
            System.Diagnostics.Debug.Assert(success);
        }
#endif

        #endregion
        
        #region Google AdMob
#if ADMOB
        private AdComponent CreateAdMobControl(AdInformation adInfo)
        {
            GoogleAdMobComponent.Initialize(game, adInfo.AppID);
            return GoogleAdMobComponent.Current;
        }
#endif  
        #endregion


        #region iAd

        #if iOS
        private AdComponent CreateiAdControl(AdInformation adInfo)
        {
            iAdComponent.Initialize(game, adInfo.AppID);
            return iAdComponent.Current;
        }

#endif
        #endregion



        #region PubCenter

#if PUBCENTER
        private AdComponent CreatePubCentertAdControl(AdInformation adInfo)
        {
            try
            {
                // NOTE: If the code below fails to compile it is
                // because you're running with the old version of
                // the Microsoft Advertising SDK.

                // Initialize the AdGameComponent with your ApplicationId and add it to the game.
                if (!AdPubcenterComponent.Initialised)
                    AdPubcenterComponent.Initialize(this.Game, adInfo.AppID);

                return AdPubcenterComponent.Current;
            }
            catch (Exception) { return null; }
        }

#endif // PUBCENTER
        #endregion

        #region AdDuplex
#if ADDUPLEX
        private GameComponent CreateAdDuplexControl()
        {
            try
            {
                AdDuplexComponent.Initialize(this.Game, AdDuplexAppId);
                AdDuplexComponent.Current.AdLoaded += Current_AdDuplex_AdLoaded;
                AdDuplexComponent.Current.AdLoadingFailed += Current_AdDuplex_AdLoadingFailed;
                AdDuplexComponent.Current.AdClicked += Current_AdDuplex_AdClicked;

                AdDuplexComponent.Current.IsTest = IsTest;

                return AdDuplexComponent.Current;
            }
            catch (Exception)
            {
                return null;
            }
        }

        void Current_AdDuplex_AdClicked(object sender, AdDuplex.AdClickEventArgs e)
        {
            //Need to Investigate CLick Link operation
            //MarketplaceDetailTask MD = new MarketplaceDetailTask();
            //MD.ContentIdentifier = e.Ad.MarketplaceAppId;
            //MD.Show();
            OnLog(string.Format("AdDuplex Ad Clicked: {0}", e.Ad.MarketplaceAppId));
        }

        void Current_AdDuplex_AdLoadingFailed(object sender, AdDuplex.AdLoadingErrorEventArgs e)
        {
            OnLog(string.Format("AdDuplex Failed: {0}", e.Error.Message.ToString()));
            OnAdLoadFailed(AdType.AdDuplex);
        }

        void Current_AdDuplex_AdLoaded(object sender, AdDuplex.AdLoadedEventArgs e)
        {
            OnLog(string.Format("AdDuplex Success: {0}", e.ToString()));
            OnAdLoadSucceeded(AdType.AdDuplex);
        }
#endif // ADDUPLEX
        #endregion

        #region InnerActive

#if ADINNERACTIVE
        private GameComponent CreateInneractiveControl()
        {
            try
            {
                AdInneractiveComponent.Initialize(this.Game, InneractiveAppId);
                AdInneractiveComponent.Current.AdLoaded += Current_Inneractive_AdLoaded;
                AdInneractiveComponent.Current.AdLoadingFailed += Current_Inneractive_AdLoadingFailed;
                AdInneractiveComponent.Current.AdClicked += Current_Inneractive_AdClicked;

                return AdInneractiveComponent.Current;
            }
            catch (Exception)
            {
                return null;
            }
        }

        void Current_Inneractive_AdClicked(object sender, EventArgs e)
        {
            OnLog(string.Format("InnerActive Ad Clicked: {0}", e.ToString()));
        }

        void Current_Inneractive_AdLoadingFailed(object sender, EventArgs e)
        {
            OnLog(string.Format("InnerActive Failed: {0}", e.ToString()));
            OnAdLoadFailed(AdType.InnerActive);
        }

        void Current_Inneractive_AdLoaded(object sender, EventArgs e)
        {
            OnLog(string.Format("InnerActive Success: {0}", e.ToString()));
            OnAdLoadSucceeded(AdType.InnerActive);
        }
#endif

        #endregion

        #region MobFox
#if MOBFOX
        private GameComponent CreateMobFoxControl()
        {
            try
            {
                AdMobFoxComponent.Initialize(this.Game, MobFoxAppId);
                AdMobFoxComponent.Current.isTest = MobFoxIsTest;
                AdMobFoxComponent.Current.AdLoaded += Current_MobFox_AdLoaded;
                AdMobFoxComponent.Current.AdLoadingFailed += Current_MobFox_AdLoadingFailed;
                AdMobFoxComponent.Current.AdClicked += Current_MobFox_AdClicked;

                return AdMobFoxComponent.Current;
            }
            catch (Exception)
            {
                return null;
            }
        }

        void Current_MobFox_AdClicked(object sender, EventArgs e)
        {
            OnLog(string.Format("MobFox Ad Clicked: {0}", e.ToString()));
        }

        void Current_MobFox_AdLoadingFailed(object sender, EventArgs e)
        {
            OnLog(string.Format("MobFox Failed: {0}", e.ToString()));
            OnAdLoadFailed(AdType.MobFox);
        }

        void Current_MobFox_AdLoaded(object sender, EventArgs e)
        {
            OnLog(string.Format("MobFox Success: {0}", e.ToString()));
            OnAdLoadSucceeded(AdType.MobFox);
        }
#endif

        #endregion

        #region Smaato
#if SMATTO
        private GameComponent CreateSmaatoControl()
        {
            try
            {
                AdSmaatoComponent.Initialize(this.Game, SmaatoAppId,SmaatoPublisherId);
                AdSmaatoComponent.Current.AdLoaded += Current_Smaato_AdLoaded;
                AdSmaatoComponent.Current.AdLoadingFailed += Current_Smaato_AdLoadingFailed;
                AdSmaatoComponent.Current.AdClicked += Current_Smaato_AdClicked;

                return AdSmaatoComponent.Current;
            }
            catch (Exception)
            {
                return null;
            }
        }

        void Current_Smaato_AdClicked(object sender, EventArgs e)
        {
            OnLog(string.Format("Smaato Ad Clicked: {0}", e.ToString()));
        }

        void Current_Smaato_AdLoadingFailed(object sender, string ErrorCode, string ErrorDescription)
        {
            OnLog(string.Format("Smaato Failed: {0} - {1}", ErrorCode.ToString(), ErrorDescription.ToString()));
            OnAdLoadFailed(AdType.Smaato);
        }

        void Current_Smaato_AdLoaded(object sender, EventArgs e)
        {
            OnLog(string.Format("Smaato Success: {0}", e.ToString()));
            OnAdLoadSucceeded(AdType.Smaato);
        }
#endif // SMATTO
        #endregion

        #endregion

    }
}
